package ch.bfh.abcmmh.controller.operating.vehiclepool;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;

import org.apache.log4j.Logger;

import ch.bfh.abcmmh.controller.logging.TSLoggingFactory;
import ch.bfh.abcmmh.controller.operating.LogicController;
import ch.bfh.abcmmh.controller.operating.clock.Clock;
import ch.bfh.abcmmh.model.cars.Vehicle;
import ch.bfh.abcmmh.model.cars.specialVehicles.Bus;
import ch.bfh.abcmmh.model.cars.specialVehicles.Car;
import ch.bfh.abcmmh.model.cars.specialVehicles.Truck;
import ch.bfh.abcmmh.model.interfaces.IVehiclePool;
import ch.bfh.abcmmh.model.streetutils.Street;

public class VehiclePool extends Observable implements Observer, IVehiclePool {

	Logger logger = TSLoggingFactory.getFileLogger("street");

	protected ArrayList<Car> cars = new ArrayList<Car>();
	protected ArrayList<Bus> buses = new ArrayList<Bus>();
	protected ArrayList<Truck> trucks = new ArrayList<Truck>();

	protected int carNumber = 0;
	protected int defVehicleWidth;
	protected int variabilityMaximum;
	protected int vpCycles;
	protected int carCount;
	protected int carCountSumm;
	protected int interval;
	
	protected Street street;
	protected Street[] streets;
	protected int chance, possibilityBus, possibilityTruck, possibilityCar;
	protected int startAggressivityLevel;
	protected LogicController logicController;
	protected boolean valueChanged;
	
	protected float cmb, btmb;

	public VehiclePool(LogicController logicController, Street[] streets) {
		this.logicController = logicController;
		this.streets = streets;
		this.chance = 4;
		this.possibilityBus = 4;
		this.possibilityTruck = 4;
		this.possibilityCar = 12;
		this.startAggressivityLevel = 3;
		this.vpCycles =0;
		this.carCount =0;
		this.carCountSumm =0;
		this.interval = 0;
		this.valueChanged = false;
		this.cmb = 0.3f;
		this.btmb = 0.1f;
	}

	@Override
	public void update(Observable o, Object arg) {

		vpCycles++;
		removeAllofStreetVehicles();

		if (new Random().nextInt(chance) == 1) {

			int streetNumber = 0;
			if (streets.length > 1) {
				streetNumber = new Random().nextInt(streets.length);
			}

			if (firstStreetpartIsFree(streetNumber)) {
				putVehicleOnStreet(o, streetNumber);
				if (chance==2) {
					for (int i = 0; i < streets.length; i++) {
						if (firstStreetpartIsFree(i)) {
							putVehicleOnStreet(o, i);
						}
					}					
				}
			} else {
				for (int i = 0; i < streets.length; i++) {
					if (firstStreetpartIsFree(i)) {
						putVehicleOnStreet(o, i);
					}
				}
			}
		}
		calculateCarPerSecond();
	}

	private void putVehicleOnStreet(Observable o, int streetNumber) {
		Vehicle vehicle = carBusOrTruck(streetNumber);
		vehicle.setAgressivity(startAggressivityLevel);
		vehicle.setValueschanged(valueChanged);
		Clock clock = (Clock) o;
		clock.addObserver(vehicle);
		addVehicleToList(vehicle);
		carCount++;
		setChanged();
		notifyObservers(vehicle);
		clearChanged();
	}

	private void calculateCarPerSecond() {
		if (vpCycles% 10 == 0) {
			carCountSumm+=carCount;
			interval++;
			float result = (float)carCountSumm/(float)interval;
			logicController.setVehiclesPerSecond(result);
			carCount =0;
		}
		
	}

	private boolean firstStreetpartIsFree(int streetnumber) {
		for (int i = 0; i < 40; i++) {
			if (!streets[streetnumber].getSAttribute(i).isFree()) {
				return false;
			}
		}

		return true;
	}

	private Vehicle carBusOrTruck(int streetNumber) {
		Random random = new Random();
		int rv = random.nextInt(20);
		// workaround
		if (possibilityBus>=possibilityTruck) {
			possibilityTruck = possibilityBus+possibilityTruck;
		}
		
		if (rv < possibilityBus) {
			return new Bus(this.streets[streetNumber], 70, 0, 0, streetNumber, btmb);
		} 
		
		if (rv < possibilityTruck) {
			return new Truck(this.streets[streetNumber], 70, 0, 0, streetNumber, btmb);
		}
		return new Car(this.streets[streetNumber], 70, 0, 0, streetNumber, cmb);
	}

	private void removeAllofStreetVehicles() {
		Car gone = null;
		for (Car c : cars) {
			if (!c.isOnStreet()) {
				gone = c;
				break;
			}
		}
		cars.remove(gone);
		Bus bgone = null;
		for (Bus b : buses) {
			if (!b.isOnStreet()) {
				bgone = b;
				break;
			}
		}
		buses.remove(bgone);
		Truck tgone = null;
		for (Truck t : trucks) {
			if (!t.isOnStreet()) {
				tgone = t;
				break;
			}
		}
		trucks.remove(tgone);
	}

	public boolean firstStreetElementfree() {
		return street.getSAttribute(0).isFree();
	}

	private void addVehicleToList(Vehicle vehicle) {
		if (Car.class.isInstance(vehicle)) {
			cars.add((Car) vehicle);
		}
		if (Bus.class.isInstance(vehicle)) {
			buses.add((Bus) vehicle);
		}
		if (Truck.class.isInstance(vehicle)) {
			trucks.add((Truck) vehicle);
		}
	}

	public void updateCarMaybe(float cmb) {
		for (Car c : cars) {
			c.setMaybe(cmb);
		}
		this.cmb = cmb;
	}
	public void updateBTMaybe(float btmb) {
		for (Bus b : buses) {
			b.setMaybe(btmb);
		}
		for (Truck t : trucks) {
			t.setMaybe(btmb);
		}
		this.btmb = btmb;;
	}
	
	
	@Override
	public ArrayList<Car> getAllCars() {
		return this.cars;
	}

	@Override
	public ArrayList<Bus> getAllBuses() {
		return this.buses;
	}

	@Override
	public ArrayList<Truck> getAllTrucks() {
		return this.trucks;
	}

	public int getChance() {
		return chance;
	}

	public void setChance(int chance) {
		int nc = chance + 2;
		if (chance > 1 && chance < 22) {
			this.chance = nc;
		}
	}

	public int getPossibilityBus() {
		return possibilityBus;
	}

	public void setPossibilityBus(int possibilityBus) {
		this.possibilityBus = possibilityBus;
	}

	public int getPossibilityTruck() {
		return possibilityTruck;
	}

	public void setPossibilityTruck(int possibilityTruck) {
		this.possibilityTruck = possibilityTruck;
	}

	@Override
	public int getAverageSpeedofCars() {
		int totalspeed = 0;
		for (Car c : cars) {
			totalspeed += c.getSpeedinKM();
		}
		if (cars.size() != 0) {
			return totalspeed / cars.size();
		} else {
			return 0;
		}
	}

	public int getVariabilityMaximum() {
		return variabilityMaximum;
	}

	public void setVariabilityMaximum(int variabilityMaximum) {
		this.variabilityMaximum = variabilityMaximum;
	}

	public int getPossibilityCar() {
		return possibilityCar;
	}

	public void setPossibilityCar(int possibilityCar) {
		this.possibilityCar = possibilityCar;
	}

	public int getStartAggressivityLevel() {
		return startAggressivityLevel;
	}

	public void setStartAggressivityLevel(int startAggressivityLevel) {
		this.startAggressivityLevel = startAggressivityLevel;
	}

	public boolean isValueChanged() {
		return valueChanged;
	}

	public void setValueChanged(boolean valueChanged) {
		this.valueChanged = valueChanged;
	}

	public float getCmb() {
		return cmb;
	}

	public void setCmb(float cmb) {
		this.cmb = cmb;
	}

	public float getBtmb() {
		return btmb;
	}

	public void setBtmb(float btmb) {
		this.btmb = btmb;
	}

	@Override
	public int getAverageSpeedofBuses() {
		int totalspeed = 0;
		for (Bus b : buses) {
			totalspeed += b.getSpeedinKM();
		}
		if (buses.size() != 0) {
			return totalspeed / buses.size();
		} else {
			return 0;
		}
	}

	@Override
	public int getAverageSpeedofTrucks() {
		int totalspeed = 0;
		for (Truck t : trucks) {
			totalspeed += t.getSpeedinKM();
		}
		if (trucks.size() != 0) {
			return totalspeed / trucks.size();
		} else {
			return 0;
		}
	}
	
}
